using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using Microsoft.WowAddonStudio.Lua.RuntimeService.WowBench;

namespace Microsoft.WowAddonStudio.Lua.RuntimeService.ShellHost
{
	/// <summary>
	/// Base class for IShellHost implementations.
	/// </summary>
	[Serializable]
	public abstract partial class BaseShellHost : IShellHost
	{
		#region Protected Variables

		/// <summary>
		/// Default Encoding for ConsoleShell
		/// </summary>
		protected readonly Encoding consoleEncoding;
		protected const int BufferSize = 1024;
		protected readonly byte[] errorBuffer = new byte[BufferSize];
		protected readonly byte[] outputBuffer = new byte[BufferSize];

		/// <summary>
		/// Object to be used for thread syncing
		/// </summary>
		[NonSerialized]
		protected readonly object syncObject = new Object();

		/// <summary>
		/// Shell arguments.
		/// </summary>
		protected string arguments;

		/// <summary>
		/// Last command executed by user
		/// </summary>
		protected string lastCommand;

		/// <summary>
		/// Current process Id.
		/// </summary>
		protected int processId;

		/// <summary>
		/// A descriptive name for the shell.
		/// </summary>
		protected string shellName;

		/// <summary>
		/// Object referring to running copy of executable.
		/// </summary>
		[NonSerialized]
		protected Process shellProcess;

		/// <summary>
		/// Shell UI interface object
		/// </summary>
		[NonSerialized]
		protected IOutputWriter writer;

		#endregion

		#region Protected Members

		/// <summary>
		/// Initializes a new instance of the <see cref="BaseShellHost"/> class.
		/// </summary>
		protected BaseShellHost()
		{
			consoleEncoding = Encoding.GetEncoding("ibm850");
		}

		/// <summary>
		/// Gets StandardInput stream reference.
		/// </summary>
		protected StreamWriter InputStream
		{
			get
			{
				if (shellProcess != null)
				{
					return shellProcess.StandardInput;
				}
				throw new InvalidOperationException();
			}
		}

		/// <summary>
		/// Raise OnProcessExited event.
		/// </summary>
		protected void RaiseOnProcessExited()
		{
			if (onProcessExited != null)
				onProcessExited(this, new ProcessExitedEventArgs(shellProcess.ExitCode, ShellName));
		}

		#endregion


		#region IShellHost Members

		protected event EventHandler<ProcessExitedEventArgs> onProcessExited;

		/// <summary>
		/// Fires when the <see cref="System.Diagnostics.Process"/> exited.
		/// </summary>
		public event EventHandler<ProcessExitedEventArgs> OnProcessExited
		{
			add { onProcessExited += value; }
			remove { onProcessExited -= value; }
		}

		/// <summary>
		/// A descriptive name for the <see cref="IShellHost"/>.
		/// </summary>
		public virtual string ShellName
		{
			get { return shellName; }
		}

		/// <summary>
		/// The current state of <see cref="IShellHost"/>.
		/// </summary>
		public ShellHostState ShellState { get; protected set; }

		/// <summary>
		/// Executes a command on the <see cref="IShellHost"/>.
		/// </summary>
		/// <param name="command">Command to execute</param>
		public abstract void ExecuteCommand(string command);


		/// <remarks>
		/// Currently this is not implemented completely for cmd.exe host because there is no
		/// way to access environment variables within the shell process
		/// without executing: echo input command on the shell itself.
		/// Currently only the environment variables from VS process is being read
		/// </remarks>
		public virtual string ExpandEnvironmentVariables(string input)
		{
			return Environment.ExpandEnvironmentVariables(input);
		}

		/// <summary>
		/// This method is called when ShellTextBox creates the host instance for the first time. 
		/// </summary>
		/// <param name="startingDirectory">Preferred directory to start in.</param>
		/// <param name="outputWriter">The <see cref="IOutputWriter"/> parameter.</param>
		/// <remarks>This method will only be called once at the beginning.</remarks>
		public virtual bool LaunchShellProcess(IOutputWriter outputWriter, string startingDirectory)
		{
			return LaunchShellProcess(outputWriter, startingDirectory, ShellCommandSet.CommandSetLine.Host);
		}

		/// <summary>
		/// This method is called when ShellTextBox creates the host instance for the first time. 
		/// </summary>
		/// <param name="startingDirectory">Preferred directory to start in.</param>
		/// <param name="outputWriter">The <see cref="IOutputWriter"/> parameter.</param>
		/// <param name="executableName">The name of executable to load.</param>
		/// <remarks>This method will only be called once at the beginning.</remarks>
		public abstract bool LaunchShellProcess(IOutputWriter outputWriter, string startingDirectory,
		                                        string executableName);


		/// <summary>
		/// This method is called when ShellTextBox creates the host instance for the first time. 
		/// </summary>
		/// <param name="startingDirectory">Preferred directory to start in.</param>
		/// <param name="outputWriter">The <see cref="IOutputWriter"/> parameter.</param>
		/// <param name="executableName">The name of executable to load.</param>
		/// <param name="argument">The argument for executable.</param>
		/// <remarks>This method will only be called once at the beginning.</remarks>
		public abstract bool LaunchShellProcess(IOutputWriter outputWriter, string startingDirectory,
		                                        string executableName,
		                                        string argument);


		/// <summary>
		/// Gets the current working directory of the user.
		/// It works only with Windows Command prompt.
		/// </summary>
		/// <param name="currentLine">Current line in the Output buffer.</param>
		/// <returns>The current directory name.</returns>
		protected virtual string GetCurrentWorkingDirectory(string currentLine)
		{
			if (currentLine == null)
				throw new ArgumentNullException("currentLine");

			currentLine = currentLine.Substring(0, currentLine.IndexOf(@">"));
			return currentLine;
		}

		///<summary>
		///Performs application-defined tasks associated with freeing, releasing,
		/// or resetting unmanaged resources.
		///</summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		#endregion

		/// <summary>
		/// Dispose unused resources.
		/// </summary>
		/// <param name="disposing"></param>
		protected virtual void Dispose(bool disposing)
		{
			try
			{
				ShellState = ShellHostState.Closed;
				if (shellProcess != null && !shellProcess.HasExited)
				{
					shellProcess.EnableRaisingEvents = false;
					shellProcess.Kill();
				}
			}
			catch (Exception e)
			{
				Trace.WriteLine(e);
			}
			onProcessExited = null;
		}
	}
}